home *** CD-ROM | disk | FTP | other *** search
/ Windows Expert / Windows Expert.iso / windownt / wvnsrc75.zip / WVUTIL.C < prev   
C/C++ Source or Header  |  1993-02-03  |  28KB  |  850 lines

  1. /*-- WVUTIL.C -- File containing utility routines.
  2.  */
  3.  
  4. #include "windows.h"
  5. #include "wvglob.h"
  6. #include "winvn.h"
  7. #ifndef MAC
  8. #include "winundoc.h"
  9. #include <ctype.h>
  10. #endif
  11.  
  12. /* temporary test */
  13. char far *mylstrcpy(char *ptr1, char *ptr2);
  14. char * get_xhdr_line (char * line);
  15. char * parse_usenet_date (char * date);
  16.  
  17. /*--- function GetNum --------------------------------------------
  18.  *
  19.  *  Cracks off a positive integer number from a string.
  20.  *
  21.  *  Entry    *ptr  is the character position to start scanning
  22.  *                 for an integer
  23.  *
  24.  *  Exit     *ptr  is the character position at which we stopped
  25.  *                 scanning (because of a non-digit).
  26.  *           *num  is the cracked off number.
  27.  *           Returns TRUE iff we got a number.
  28.  */
  29. BOOL
  30. GetNum(ptr,num)
  31. char **ptr;
  32. long int *num;
  33. {
  34.    BOOL gotit = FALSE;
  35.  
  36.    /* Skip initial spaces                                            */
  37.  
  38.    while((**ptr) && **ptr == ' ') (*ptr)++;
  39.  
  40.    *num = 0;
  41.    while(**ptr && isdigit(**ptr)) {
  42.       *num = 10*(*num) + (**ptr - '0');
  43.       gotit = TRUE;
  44.       (*ptr)++;
  45.    }
  46.    return(gotit);
  47. }
  48.  
  49. char * get_xhdr_line (char *line)
  50. {
  51.   char * cptr;
  52. /* skip past the art # and space */
  53.   for(cptr=line; isdigit(*cptr); cptr++);
  54.   for(;*cptr == ' ';cptr++);
  55.   return (cptr);
  56. }
  57.  
  58. /*-- function StrToRGB -------------------------------------------------
  59.  *
  60.  *  Takes an ASCII string of the form "r,g,b" where r, g, and b are
  61.  *  decimal ASCII numbers, and converts it to an RGB color number.
  62.  */
  63. DWORD
  64. StrToRGB(cstring)
  65. char *cstring;
  66. {
  67.    BYTE red, green, blue;
  68.    long int lred, lgreen, lblue;
  69.  
  70.    GetNum(&cstring,&lred);
  71.    cstring++;
  72.    GetNum(&cstring,&lgreen);
  73.    cstring++;
  74.    GetNum(&cstring,&lblue);
  75.    red = (BYTE) lred; green = (BYTE) lgreen; blue = (BYTE) lblue;
  76. #ifndef MAC
  77.    return(RGB(red,green,blue));
  78. #else
  79.    return((DWORD) red+green+blue);
  80. #endif
  81. }
  82.  
  83. /* This was lifted from ANU news. */
  84.  
  85. char *
  86. parse_usenet_date(char * s)
  87. {
  88.   char *cp, mon[80], pdate[15];
  89.   int dom = 0, yr = 0, hr = 0, mn = 0, sc = 0, cvttime;
  90.  
  91.   if (!s || !*s) return(0);
  92.   if (cp = strchr(s,',')) s = ++cp;
  93.   while (isspace(*s)) s++;
  94.   *mon = '\0';
  95.   if (isdigit(*s)) {
  96.     sscanf(s,"%d %s %d %d:%d:%d",&dom,mon,&yr,&hr,&mn,&sc);
  97.     if (yr < 100 ) yr += 1900;
  98.     }
  99.   else sscanf(s,"%*s %s %d %d:%d:%d %d",mon,&dom,&hr,&mn,&sc,&yr);
  100.  
  101.   if (!dom || !yr || !*(cp = mon)) return(0);
  102.   if ((dom <= 0) || (dom >= 32)) return(0);
  103.   if ((yr < 1980) || (yr > 2020)) return(0);
  104.   if (strlen(mon) > 10) return(0);
  105.   if ((hr < 0) || (hr > 23)) return(0);
  106.   if ((mn < 0) || (mn > 59)) return(0);
  107.   if ((sc < 0) || (sc > 59)) return(0);
  108.  
  109. /*  while (*cp) { *cp = toupper(*cp); ++cp; } */
  110. /*  sprintf(s,"%d-%s-%d %d:%d:%d",dom,mon,yr,hr,mn,sc); */
  111.   sprintf(s,"%s %2.2d",mon,dom);
  112.   return(s);
  113. }
  114.  
  115.  
  116. /*-- function DoCommInput ---------------------------------------
  117.  *
  118.  *
  119.  */
  120. void
  121. DoCommInput()
  122. {
  123.    int ch;
  124.  
  125.    while((ch = MRRReadComm()) >= 0) {
  126.       /*   putchar(ch); */  /* debug */
  127.       if(ch == IgnoreCommCh) {
  128.       } else if(ch == EOLCommCh) {
  129.          *CommLinePtr = '\0';
  130.          DoCommState();
  131.          CommLinePtr = CommLineIn;
  132.       } else {
  133.          *(CommLinePtr++) = (char) ch;
  134.          if(CommLinePtr == CommLineLWAp1) CommLinePtr--;
  135.       }
  136.    }
  137. }
  138.  
  139. /*-- function DoCommState ----------------------------------------------
  140.  *
  141.  *  Function to implement an FSA to process incoming lines from
  142.  *  the server.
  143.  *  This function is called once for each line from the server.
  144.  *
  145.  *    Entry    CommLineIn  is a zero-terminated line received from
  146.  *                         the server.
  147.  *             CommState   is the current state of the FSA.
  148.  */
  149. void
  150. DoCommState()
  151. {
  152.    TypLine far *LinePtr;
  153.    TypBlock far *BlockPtr;
  154.    TypArticle *artptr;
  155.    TypArticle far *MyArtPtr;
  156.    HANDLE hBlock;
  157.    HWND hWndPostEdit;
  158.    unsigned int Offset;
  159.    TypLineID MyLineID;
  160.    int retcode;
  161.    int ih, found;
  162.    unsigned int estnum;
  163.    long int first,last;
  164.    long int artnum;
  165.    int lineord;
  166.    int mylen;
  167.    int col;
  168.    int mbcode;
  169.    BOOL done=FALSE;
  170.    BOOL dolist;
  171.    char group[MAXINTERNALLINE];
  172.    char mybuf[MAXINTERNALLINE];
  173.    char artline[MAXINTERNALLINE];
  174.    char scanline[MAXINTERNALLINE];
  175.    char *cptr, *cdest;
  176.    char far *lpsz;
  177.    char indicator;
  178.    HDC hDC;
  179.    long int PrevHighArt;
  180.    HANDLE header_handle;
  181.    TypHeader * headers;
  182.    TypGroup far * GroupDoc;
  183.    long int OldHighestSeen;
  184.  
  185.    if(CommDoc) {
  186.  
  187.       switch(CommState) {
  188.          case ST_NONE:
  189.             break;
  190.  
  191.          case ST_ESTABLISH_COMM:
  192.             retcode = 0;
  193.             sscanf(CommLineIn,"%u",&retcode);
  194.             if(retcode == 200 || retcode == 201) {/* was 500 from induced error */
  195.                dolist = DoList;
  196.                if(dolist == ID_DOLIST_ASK-ID_DOLIST_BASE) {
  197.                   dolist = DialogBox(hInst,"WinVnDoList",hWndConf,lpfnWinVnDoListDlg);
  198.                }
  199.                if(dolist) {
  200.                   StartList();
  201.                } else {
  202.                   CommState = ST_NONE;
  203.                   CommBusy = FALSE;
  204.                   Initializing = INIT_DONE;
  205.                }
  206.  
  207.                InvalidateRect(hWndConf,NULL,FALSE);
  208.             }
  209.         else {
  210.             MessageBox (hWndConf, CommLineIn, "Error", MB_OK | MB_ICONHAND);
  211.         MRRCloseComm ();
  212.         PostQuitMessage (0);
  213.         }
  214.  
  215.             break;
  216.  
  217.          case ST_LIST_RESP:
  218.             retcode = 0;
  219.             sscanf(CommLineIn,"%d",&retcode);
  220.             if(retcode != 215) break;
  221.             CommState = ST_LIST_GROUPLINE;
  222.             RcvLineCount = 0;
  223.             break;
  224.  
  225.          case ST_LIST_GROUPLINE:
  226.             if(strcmp(CommLineIn,".") == 0) {
  227.                CommState = ST_NONE;
  228.                CommBusy = FALSE;
  229.                Initializing = INIT_DONE;
  230.                InvalidateRect(hWndConf,NULL,FALSE);
  231.  
  232.                ProcEndList();
  233.             } else {
  234.                ProcListLine((unsigned char *)CommLineIn);
  235.             }
  236.             break;
  237.  
  238.          case ST_GROUP_RESP:
  239.             retcode = 0;
  240.             sscanf(CommLineIn,"%u %u %ld %ld %s",&retcode,&estnum,&first,&last,group);
  241.         if (retcode == 415) {
  242.             MessageBox (hWndConf, "No Such Newsgroup", "Error", MB_OK | MB_ICONHAND);
  243.             CommBusy = FALSE;
  244.             CommState = ST_NONE;
  245.         break;
  246.         }
  247.             if(retcode < 100) break;
  248.  
  249.           LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  250.  
  251.           if (estnum > 0)
  252.           {
  253.           header_handle = GlobalAlloc (GMEM_MOVEABLE, (long)((sizeof (TypHeader)) * (long)(estnum)) + sizeof (char *));  /* How risky is this? */
  254.         (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->header_handle) = header_handle;
  255.           }
  256.  
  257.             (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->ServerEstNum) = estnum;
  258.             (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->ServerFirst ) = first;
  259.             (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->ServerLast  ) = last;
  260.             GlobalUnlock(BlockPtr->hCurBlock);
  261.  
  262.             mylen = sprintf(mybuf,"XHDR from %ld-%ld\r",first,999999L);
  263.             PutCommLine(mybuf,mylen);
  264.             CommState = ST_XHDR_FROM_START;
  265.  
  266.             break;
  267.  
  268.           case ST_XHDR_FROM_START:
  269.             retcode = 0;
  270.             sscanf(CommLineIn,"%d",&retcode);
  271.             if(retcode < 100) break;
  272.             CommState = ST_XHDR_FROM_DATA;
  273.         CommDoc->ActiveLines = 0;
  274.         break;
  275.  
  276.     case ST_XHDR_FROM_DATA:
  277.             if(strcmp(CommLineIn,".") == 0) {
  278.                    LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  279.                 (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->total_headers) = CommDoc->ActiveLines;
  280.         first = (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->ServerFirst );
  281.                 GlobalUnlock(BlockPtr->hCurBlock);
  282.         CommDoc->ActiveLines = 0;
  283.  
  284.         /* Now ask for the date lines */
  285.                 mylen = sprintf(mybuf,"XHDR date %ld-%ld\r",first,999999L);
  286.                 PutCommLine(mybuf,mylen);
  287.                 CommState = ST_XHDR_DATE_START;
  288.             }
  289.             else {
  290.         /* Access the Group struct, get HANDLE for header data */
  291.                    LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  292.                 header_handle = ((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->header_handle;
  293.                 GlobalUnlock(BlockPtr->hCurBlock);
  294.  
  295.         /* Lock the header data */
  296.                 headers = (TypHeader *) ((char *) GlobalLock (header_handle) + sizeof(char *));
  297.  
  298.         sscanf (CommLineIn, "%ld", &artnum);
  299.                 headers[CommDoc->ActiveLines].number = artnum;
  300.  
  301.                 mylstrcpy(headers[CommDoc->ActiveLines].from,
  302.                           (char far *) get_xhdr_line (CommLineIn));
  303.  
  304.         GlobalUnlock (header_handle);
  305.                 CommDoc->ActiveLines++;
  306.  
  307.             }
  308.  
  309.         break;
  310.  
  311.           case ST_XHDR_DATE_START:
  312.             retcode = 0;
  313.             sscanf(CommLineIn,"%d",&retcode);
  314.             if(retcode < 100) break;
  315.             CommState = ST_XHDR_DATE_DATA;
  316.         CommDoc->ActiveLines = 0;
  317.         break;
  318.  
  319.     case ST_XHDR_DATE_DATA:
  320.             if(strcmp(CommLineIn,".") == 0) {
  321.                    LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  322.                 (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->total_headers) = CommDoc->ActiveLines;
  323.         first = (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->ServerFirst );
  324.                 GlobalUnlock(BlockPtr->hCurBlock);
  325.         CommDoc->ActiveLines = 0;
  326.  
  327.         /* Now ask for the #of lines */
  328.                 mylen = sprintf(mybuf,"XHDR lines %ld-%ld\r",first,999999L); 
  329.                 PutCommLine(mybuf,mylen);
  330.                 CommState = ST_XHDR_LINES_START; 
  331.             }
  332.             else {
  333.         /* Access the Group struct, get HANDLE for header data */
  334.                    LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  335.                 header_handle = ((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->header_handle;
  336.                 GlobalUnlock(BlockPtr->hCurBlock);
  337.  
  338.         /* Lock the header data */
  339.                 headers = (TypHeader *)((char *) GlobalLock (header_handle) + sizeof (char *));
  340.  
  341.                 mylstrcpy(headers[CommDoc->ActiveLines].date,
  342.                           (char far *) (parse_usenet_date (get_xhdr_line (CommLineIn))));
  343.  
  344.         GlobalUnlock (header_handle);
  345.                 CommDoc->ActiveLines++;
  346.  
  347.             }
  348.  
  349.         break;
  350.  
  351.           case ST_XHDR_LINES_START:
  352.             retcode = 0;
  353.             sscanf(CommLineIn,"%d",&retcode);
  354.             if(retcode < 100) break;
  355.             CommState = ST_XHDR_LINES_DATA;
  356.         CommDoc->ActiveLines = 0;
  357.         break;
  358.  
  359.     case ST_XHDR_LINES_DATA:
  360.             if(strcmp(CommLineIn,".") == 0) {
  361.                    LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  362.                 (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->total_headers) = CommDoc->ActiveLines;
  363.         first = (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->ServerFirst );
  364.                 GlobalUnlock(BlockPtr->hCurBlock);
  365.         CommDoc->ActiveLines = 0;
  366.  
  367.         /* Now ask for the subject headers */
  368.                 mylen = sprintf(mybuf,"XHDR subject %ld-%ld\r",first,999999L); 
  369.                 PutCommLine(mybuf,mylen);
  370.                 CommState = ST_XHDR_RESP; 
  371.             }
  372.             else {
  373.         /* Access the Group struct, get HANDLE for header data */
  374.                    LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  375.                 header_handle = ((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->header_handle;
  376.                 GlobalUnlock(BlockPtr->hCurBlock);
  377.  
  378.         /* Lock the header data */
  379.                 headers = (TypHeader *)((char *) GlobalLock (header_handle) + sizeof (char *));
  380.  
  381.         sscanf (CommLineIn, "%ld %Fd", &artnum, &(headers[CommDoc->ActiveLines].lines));
  382.  
  383.         GlobalUnlock (header_handle);
  384.                 CommDoc->ActiveLines++;
  385.  
  386.             }
  387.  
  388.         break;
  389.  
  390.           case ST_XHDR_REF_START:
  391.             retcode = 0;
  392.             sscanf(CommLineIn,"%d",&retcode);
  393.             if(retcode < 100) break;
  394.             CommState = ST_XHDR_REF_DATA;
  395.         CommDoc->ActiveLines = 0;
  396.         break;
  397.  
  398.     case ST_XHDR_REF_DATA:
  399.             if(strcmp(CommLineIn,".") == 0) {
  400.                    LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  401.                 (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->total_headers) = CommDoc->ActiveLines;
  402.         first = (((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->ServerFirst );
  403.                 GlobalUnlock(BlockPtr->hCurBlock);
  404.         CommDoc->ActiveLines = 0;
  405.  
  406.         /* Now ask for the subject lines */
  407.                 mylen = sprintf(mybuf,"XHDR subject %ld-%ld\r",first,999999L);
  408.                 PutCommLine(mybuf,mylen);
  409.                 CommState = ST_XHDR_RESP;
  410.             }
  411.             else {
  412.         /* Access the Group struct, get HANDLE for header data */
  413.                    LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  414.                 header_handle = ((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->header_handle;
  415.                 GlobalUnlock(BlockPtr->hCurBlock);
  416.  
  417.         /* Lock the header data */
  418.                 headers = (TypHeader *)((char *) GlobalLock (header_handle) + sizeof (char *));
  419.  
  420.                 mylstrncpy(headers[CommDoc->ActiveLines].references,
  421.                           (char far *) (get_xhdr_line (CommLineIn)),
  422.                            30);        /* bad, hardcoded. */
  423.  
  424.         GlobalUnlock (header_handle);
  425.                 CommDoc->ActiveLines++;
  426.  
  427.             }
  428.  
  429.         break;
  430.  
  431.          case ST_XHDR_RESP:
  432.             retcode = 0;
  433.             sscanf(CommLineIn,"%d",&retcode);
  434.             if(retcode < 100) break;
  435.             CommState = ST_XHDR_SUBJ;
  436.             break;
  437.  
  438.          case ST_XHDR_SUBJ:
  439.             if(strcmp(CommLineIn,".") == 0) {
  440.                CommState = ST_IN_GROUP;
  441.                CommBusy = FALSE;
  442.  
  443.         /* release the mouse that is captured to the usenet window */
  444.         ReleaseCapture ();
  445.  
  446.         CommDoc->ActiveLines = 0;
  447.                /* Fetch this group's line in NetDoc so we can get the
  448.                 * group's name for the window's title bar.
  449.                 */
  450.                LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  451.                lpsz = (char far *) ( ((char far *)LinePtr) +
  452.                 sizeof(TypLine)+ sizeof(TypGroup) ) ;
  453.                mylstrncpy(group,lpsz,MAXGROUPNAME);
  454.                sprintf(mybuf,"%s (%u articles)",group,CommDoc->TotalLines);
  455.                SetWindowText(CommDoc->hDocWnd,mybuf);
  456.  
  457.                /* If we have information from NEWSRC on the highest-
  458.                 * numbered article previously seen, position the window
  459.                 * so the new articles can be seen without scrolling.
  460.                 */
  461.  
  462.                 /* reinsert this code */
  463.  
  464.                 InvalidateRect(CommDoc->hDocWnd,NULL,FALSE);
  465.             } else {
  466.  
  467.                artnum = 0;
  468.                sscanf(CommLineIn,"%ld",&artnum);
  469.                if(artnum) {
  470.                 LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  471.  
  472.         /* inside the lock, can access the GroupStruct */
  473.                 header_handle = ((TypGroup far *) ((char far *) LinePtr + sizeof(TypLine)) )->header_handle;
  474.                    headers = (TypHeader *) ((char *) GlobalLock (header_handle) + sizeof(char *)) ;
  475.  
  476.         /* update the seen thing. */
  477.  
  478.                 headers[CommDoc->ActiveLines].Selected= FALSE ;
  479.                 headers[CommDoc->ActiveLines].ArtDoc  = (TypDoc *) NULL;
  480.         headers[CommDoc->ActiveLines].Seen =
  481.            WasArtSeen (artnum,(TypGroup far *)( ((char far *)LinePtr) + sizeof(TypLine) ) );
  482.  
  483.                 UnlockLine(BlockPtr,LinePtr,&(CommDoc->hParentBlock),&(CommDoc->ParentOffset),&(CommDoc->ParentLineID));
  484.  
  485.                 mylstrcpy(headers[CommDoc->ActiveLines].subject,
  486.                           get_xhdr_line (CommLineIn));
  487.  
  488.         GlobalUnlock (header_handle);
  489.  
  490.         CommDoc->ActiveLines++;
  491.  
  492.         CommDoc->TotalLines = CommDoc->ActiveLines;
  493.  
  494.                 /* Cause the window to be repainted.
  495.                  * Also, every UPDATE_TITLE_FREQ lines, update the
  496.                  * window title to let the user know how far we
  497.                  * have gotten.
  498.                  */
  499.                 InvalidateRect(CommDoc->hDocWnd,NULL,FALSE);
  500.                 if((++RcvLineCount)%UPDATE_TITLE_FREQ == 0) {
  501.                    sprintf(mybuf,"Retrieving %uth article of ",RcvLineCount);
  502.                    LockLine(CommDoc->hParentBlock,CommDoc->ParentOffset,CommDoc->ParentLineID,&BlockPtr,&LinePtr);
  503.                    lpsz = (char far *) ( ((char far *)LinePtr) +
  504.                     sizeof(TypLine)+ sizeof(TypGroup) ) ;
  505.                    lstrcat(mybuf,lpsz);
  506.                    SetWindowText(CommDoc->hDocWnd,mybuf);
  507.                    GlobalUnlock(BlockPtr->hCurBlock);
  508.                /*
  509.                 *  if(++TimesWndUpdated <= MAX_IMMEDIATE_UPDATE) {
  510.                 *     InvalidateRect(CommDoc->hDocWnd,NULL,FALSE);
  511.                 *  }
  512.                 */
  513.                    UpdateWindow(CommDoc->hDocWnd);
  514.                 }
  515.              }
  516.           }
  517.  
  518.           break;
  519.  
  520.          case ST_IN_GROUP:
  521.             break;
  522.  
  523.          case ST_ARTICLE_RESP:
  524.             retcode = 0;
  525.             sscanf(CommLineIn,"%d",&retcode);
  526.             if(retcode < 100) break;
  527.             CommState = ST_REC_ARTICLE;
  528.             break;
  529.  
  530.          case ST_REC_ARTICLE:
  531.             if(strcmp(CommLineIn,".") == 0) {
  532.                CommState = ST_IN_GROUP;
  533.                CommBusy = FALSE;
  534.  
  535.                 LockLine (CommDoc->ParentDoc->hParentBlock,
  536.                           CommDoc->ParentDoc->ParentOffset,
  537.                           CommDoc->ParentDoc->ParentLineID,
  538.                           &BlockPtr, &LinePtr);
  539.  
  540.                 GroupDoc = (TypGroup far *) ((char far *) LinePtr + sizeof (TypLine));
  541.  
  542.                 header_handle = GroupDoc->header_handle;
  543.                 headers = (TypHeader *) ((char *) GlobalLock (header_handle) + sizeof(char *)) ;
  544.  
  545.         lpsz = (char far *) headers[CommDoc->LastSeenLineID].subject;
  546.  
  547.         GlobalUnlock (header_handle);
  548.                mylstrncpy(group,lpsz,MAXGROUPNAME);
  549.                sprintf(mybuf,"%s (%u lines)",group,CommDoc->TotalLines);
  550.                SetWindowText(CommDoc->hDocWnd,mybuf);
  551.                InvalidateRect(CommDoc->hDocWnd,NULL,FALSE);
  552.                GlobalUnlock(BlockPtr->hCurBlock);
  553.  
  554.                /* Skip to the first line of the text of the article
  555.                 * and make sure it's visible on the screen.  This is
  556.                 * so that the user doesn't have to have the first
  557.                 * screen filled with a lengthy, worthless header.
  558.                 */
  559.                if(CommDoc->TotalLines > CommDoc->ScYLines
  560.                  && !CommDoc->TopScLineID) {
  561.                   TopOfDoc(CommDoc,&BlockPtr,&LinePtr);
  562.                   found = FALSE;
  563.                   do {
  564.                      lpsz = ((char far *)LinePtr + sizeof(TypLine) + sizeof(TypText));
  565.                      if(IsLineBlank(lpsz)) {
  566.                         found = TRUE;
  567.                         break;
  568.                      }
  569.                      if(!NextLine(&BlockPtr,&LinePtr)) break;
  570.                   } while(!found);
  571.                   NextLine(&BlockPtr,&LinePtr);
  572.  
  573.                   /* If the line is in the last screen's worth of lines, back
  574.                    * up the pointer so it points to the first line of the last
  575.                    * screen.
  576.                    */
  577.                   if(found) {
  578.                      AdjustTopSc(BlockPtr,LinePtr);
  579.                   } else {
  580.                      UnlockLine(BlockPtr,LinePtr,&hBlock,&Offset,&MyLineID);
  581.                   }
  582.                }
  583.  
  584.             } else {
  585.                /* Copy this line into an image of a textblock line,
  586.                 * expanding tabs.
  587.                 */
  588.                cdest = artline+sizeof(TypLine)+sizeof(TypText);
  589.                for(col=0,cptr=CommLineIn; *cptr &&
  590.                 col<(MAXINTERNALLINE-3*sizeof(TypLine)-sizeof(TypText)); cptr++) {
  591.                   if(*cptr == '\t') {
  592.                      do {
  593.                         *(cdest++) = ' ';
  594.                      } while (++col & 7);
  595.                   } else {
  596.                      *(cdest++) = *cptr;
  597.                      col++;
  598.                   }
  599.                }
  600.                *(cdest++) = '\0';
  601.  
  602.                mylen = (cdest-artline) + sizeof(int);
  603.                mylen += mylen%2;
  604.                ((TypText *)(artline+sizeof(TypLine)))->NameLen =
  605.                 (cdest-1) - (artline+sizeof(TypLine)+sizeof(TypText));
  606.                ((TypLine *)artline)->length = mylen;
  607.                ((TypLine *)artline)->LineID = NextLineID++;
  608.                *( (int *) (artline+mylen-sizeof(int)) ) = mylen;
  609.                LockLine(CommDoc->hCurAddBlock,CommDoc->AddOffset,CommDoc->AddLineID,&BlockPtr,&LinePtr);
  610.                AddLine((TypLine *)artline,&BlockPtr,&LinePtr);
  611.                UnlockLine(BlockPtr,LinePtr,&(CommDoc->hCurAddBlock),
  612.                 &(CommDoc->AddOffset),&(CommDoc->AddLineID));
  613.                if((CommDoc->TotalLines % UPDATE_ART_FREQ) == 0) {
  614.             InvalidateRect(CommDoc->hDocWnd,NULL,FALSE);  
  615.                }
  616.             }
  617.  
  618.             break;
  619.  
  620.          case ST_POST_WAIT_PERMISSION:
  621.             for(ih=0,found=FALSE; !found && ih<MAXPOSTWNDS; ih++) {
  622.               if(&(PostingDocs[ih]) == CommDoc) {
  623. #ifndef MAC
  624.                  hWndPostEdit = hWndPostEdits[ih];
  625. #else
  626.   /* mrr add here */
  627. #endif
  628.                  found = TRUE;
  629.                  break;
  630.               }
  631.             }
  632.             retcode = 0;
  633.             sscanf(CommLineIn,"%d",&retcode);
  634.             if(retcode <= 0) {
  635.                break;
  636.             } else if(retcode == 340) {
  637.                PostText(ih,DOCTYPE_POSTING);
  638.             } else {
  639.                MessageBox(hWndPostEdit,CommLineIn+4,"Cannot Post Article",
  640.                   MB_OK|MB_ICONEXCLAMATION);
  641.                CommBusy = FALSE;
  642.                CommState = ST_NONE;
  643.             }
  644.  
  645.             break;
  646.  
  647.          case ST_POST_WAIT_END:
  648.             for(ih=0,found=FALSE; !found && ih<MAXPOSTWNDS; ih++) {
  649.               if(&(PostingDocs[ih]) == CommDoc) {
  650. #ifndef MAC
  651.                  hWndPostEdit = hWndPostEdits[ih];
  652. #else
  653.   /* mrr add here */
  654. #endif
  655.                  found = TRUE;
  656.                  break;
  657.               }
  658.             }
  659.             retcode = 0;
  660.             sscanf(CommLineIn,"%d",&retcode);
  661.             if(retcode == 441 || retcode == 440) {
  662.                cptr = "Posting Failed";
  663.                mbcode = MB_OK|MB_ICONEXCLAMATION;
  664.                done = TRUE;
  665.             } else if(retcode == 240) {
  666.                cptr = "Article Posted OK";
  667.                mbcode = MB_OK;
  668.                done = TRUE;
  669.             }
  670.             if(done) {
  671.                CommBusy = FALSE;
  672.                CommState = ST_NONE;
  673.                MessageBox(hWndPostEdit,CommLineIn+4,cptr,mbcode);
  674.             }
  675.             break;
  676.  
  677.          case ST_MAIL_WAIT_PERMISSION:
  678.             for(ih=0,found=FALSE; !found && ih<MAXMAILWNDS; ih++) {
  679.               if(&(MailDocs[ih]) == CommDoc) {
  680. #ifndef MAC
  681.                  hWndPostEdit = hWndMailEdits[ih];
  682. #else
  683.   /* mrr add here */
  684. #endif
  685.                  found = TRUE;
  686.                  break;
  687.               }
  688.             }
  689.             retcode = 0;
  690.             sscanf(CommLineIn,"%d",&retcode);
  691.             if(retcode <= 0) {
  692.                break;
  693.             } else if(retcode == 350) {
  694.                PostText(ih,DOCTYPE_MAIL);
  695.             } else {
  696.                MessageBox(hWndPostEdit,CommLineIn+4,"Cannot Mail Message",
  697.                   MB_OK|MB_ICONEXCLAMATION);
  698.                CommBusy = FALSE;
  699.                CommState = ST_NONE;
  700.             }
  701.  
  702.             break;
  703.  
  704.          case ST_MAIL_WAIT_END:
  705.             for(ih=0,found=FALSE; !found && ih<MAXMAILWNDS; ih++) {
  706.               if(&(MailDocs[ih]) == CommDoc) {
  707. #ifndef MAC
  708.                  hWndPostEdit = hWndMailEdits[ih];
  709. #else
  710.   /* mrr add here */
  711. #endif
  712.                  found = TRUE;
  713.                  break;
  714.               }
  715.             }
  716.             retcode = 0;
  717.             sscanf(CommLineIn,"%d",&retcode);
  718.             if(retcode == 451 || retcode == 450) {
  719.                cptr = "Mailing Failed";
  720.                mbcode = MB_OK|MB_ICONEXCLAMATION;
  721.                done = TRUE;
  722.             } else if(retcode == 250) {
  723.                cptr = "Message sent OK";
  724.                mbcode = MB_OK;
  725.                done = TRUE;
  726.             }
  727.             if(done) {
  728.                CommBusy = FALSE;
  729.                CommState = ST_NONE;
  730.                MessageBox(hWndPostEdit,CommLineIn+4,cptr,mbcode);
  731.             }
  732.             break;
  733.  
  734.       case ST_GROUP_REJOIN:
  735.          CommState = ST_ARTICLE_RESP;
  736.          break;
  737.       }
  738.    }
  739. }
  740.  
  741. /*-- function WasArtSeen ---------------------------------------------
  742.  *
  743.  *  Determines whether (according to the information in a TypGroup entry)
  744.  *  a given article number was seen.
  745.  *
  746.  *  Returns TRUE iff the article has been seen.
  747.  */
  748. BOOL
  749. WasArtSeen(ArtNum,GroupPtr)
  750. long int ArtNum;
  751. TypGroup far *GroupPtr;
  752. {
  753.    TypRange far *RangePtr = (TypRange far *) ((char far *)
  754.       GroupPtr + RangeOffset(GroupPtr->NameLen));
  755.    int nr;
  756.  
  757.    for(nr=0; nr < GroupPtr->nRanges; nr++) {
  758.       if(ArtNum >= RangePtr->First && ArtNum <= RangePtr->Last) {
  759.          return(TRUE);
  760.       } else {
  761.          RangePtr++;
  762.       }
  763.    }
  764.    return(FALSE);
  765. }
  766.  
  767.  
  768. /*--- function mylstrncmp -----------------------------------------------
  769.  *
  770.  *   Just like strncmp, except takes long pointers.
  771.  */
  772. int
  773. mylstrncmp(ptr1,ptr2,len)
  774. char far *ptr1;
  775. char far *ptr2;
  776. int len;
  777. {
  778.    for(;len--;ptr1++,ptr2++) {
  779.       if(*ptr1 > *ptr2) {
  780.          return(1);
  781.       } else if(*ptr1 < *ptr2) {
  782.          return(-1);
  783.       }
  784.    }
  785.    return(0);
  786. }
  787.  
  788. /*--- function mylstrncpy -----------------------------------------------
  789.  *
  790.  *   Just like strncpy, except takes long pointers.
  791.  */
  792. char far *
  793. mylstrncpy(ptr1,ptr2,len)
  794. char far *ptr1;
  795. char far *ptr2;
  796. int len;
  797. {
  798.    char far *targ = ptr1;
  799.  
  800.    for(; --len && *ptr2; ptr1++,ptr2++) {
  801.       *ptr1 = *ptr2;
  802.    }
  803.    *ptr1 = '\0';
  804.    return(targ);
  805. }
  806.  
  807. /* this is a temporary test... */
  808. char *
  809. mylstrcpy (ptr1, ptr2)
  810. char *ptr1;
  811. char *ptr2;
  812. {
  813.   char far *targ = ptr1;
  814.   for (; *ptr2; ptr1++, ptr2++) {
  815.      *ptr1 = *ptr2;
  816.   }
  817.   *ptr1 = '\0';
  818.   return (targ);
  819. }
  820.  
  821. #if 0
  822. /*--- function lstrcmpnoblank ------------------------------------------
  823.  *
  824.  *   Like strcmp, except takes long pointers and also stops at
  825.  *   the first blank.
  826.  */
  827. int
  828. lstrcmpnoblank(str1,str2)
  829. char far **str1;
  830. char far **str2;
  831. {
  832.    register char far *s1=*str1, far *s2=*str2;
  833.  
  834.     for(;*s1 && *s2 && *s1!=' ' && *s2!=' '; s1++,s2++) {
  835.         if(*s1 > *s2) {
  836.             return (1);
  837.         } else if(*s1 < *s2) {
  838.             return (-1);
  839.         }
  840.     }
  841.     if(*s1 == *s2) {
  842.         return(0);
  843.     } else if(*s1) {
  844.         return(1);
  845.     } else {
  846.         return(-1);
  847.     }
  848. }
  849. #endif
  850.